#include "ffmpegutils.h"


#define DECODE_BUFFER_SIZE 4096


FFMPEGUtils::FFMPEGUtils()
{

}

static int count = 0;

int FFMPEGUtils::doH264Decode(AVCodecContext *codecContext, AVPacket *packet, AVFrame *frame, QFile *outFile)
{

    int res = avcodec_send_packet(codecContext,packet);

    if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) {

        return 0;
    }else if(res < 0){

        ERRMSG(res);
        qDebug() << "avcodec_send_packet" << errBuf;
        return res;
    }


    while (true) {


       res = avcodec_receive_frame(codecContext,frame);

       if(res == AVERROR(EAGAIN) || res == AVERROR_EOF) {

           return 0;
       }

//       int imageSize = av_image_get_buffer_size(codecContext->pix_fmt,codecContext->width,codecContext->height,1);
//       int writeLen = outFile->write((char *)frame->data[0],imageSize);

//       int ySize = codecContext->width * codecContext->height;
//       int uSize = ySize >> 2;
//       int vSize = ySize >> 2;
//       qDebug() << "lineSize[0]" << frame->linesize[0] << " linsSize[1]" << frame->linesize[1];
//       qDebug() << "次数 " << ++count << "ySize" << ySize << "uSize" << uSize << "vSize" << vSize;

//       outFile->write((char *)frame->data[0],ySize);
//       outFile->write((char *)frame->data[1],uSize);
//       outFile->write((char *)frame->data[2],vSize);

       int yuvHeight = codecContext->height;
       int yuvWidth = codecContext->width;
       // write Y
       for(int i = 0; i < yuvHeight; i++) {

           outFile->write((char*)(frame->data[0] + i*frame->linesize[0]),yuvWidth);
       }


       // write U
       for(int i = 0; i < (yuvHeight >> 1); i++) {

           outFile->write((char*)(frame->data[1] + i*frame->linesize[1]),yuvWidth >> 1);
       }

       // write U
       for(int i = 0; i < (yuvHeight >> 1); i++) {

           outFile->write((char*)(frame->data[2] + i*frame->linesize[2]),yuvWidth >> 1);
       }
    }

    return 0;
}

bool FFMPEGUtils::h264Decode(YUVFileSpec &yuvFileSpec, const char *outYuvPath)
{

//    AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
    AVCodec* codec = avcodec_find_decoder_by_name("h264");

    if(!codec) {

        qDebug() << "decode h264 not find";
        return false;
    }

    AVCodecParserContext* codecParserContext = av_parser_init(codec->id);

    if(!codecParserContext) {

        qDebug() << "av_parser_init error";
        return false;
    }



    AVCodecContext* codecContext = avcodec_alloc_context3(codec);

    if(codecContext == nullptr) {

        qDebug() << "avcodec_alloc_context3 error";
        return false;
    }

    AVPacket* packet = av_packet_alloc();

    if(packet == nullptr) {

        avcodec_free_context(&codecContext);
        av_parser_close(codecParserContext);
        qDebug() << "av_packet_alloc error";
        return false;
    }

    AVFrame* frame = av_frame_alloc();

    if(frame == nullptr) {

        avcodec_free_context(&codecContext);
        av_parser_close(codecParserContext);
        av_packet_free(&packet);
        qDebug() << "av_frame_alloc error";
        return false;
    }


    int res = avcodec_open2(codecContext,codec,nullptr);

    if(res < 0) {



        avcodec_free_context(&codecContext);
        av_parser_close(codecParserContext);
        av_packet_free(&packet);
        av_frame_free(&frame);

        ERRMSG(res);
        qDebug() << "avcodec_open2 error:" << errBuf;
        return false;
    }

    QFile* inFile = new QFile(yuvFileSpec.getFilePath());

    if(!inFile->open(QFile::ReadOnly)) {

        avcodec_free_context(&codecContext);
        av_parser_close(codecParserContext);
        av_packet_free(&packet);
        av_frame_free(&frame);
        return false;
    }

    QFile* outFile = new QFile(outYuvPath);

    if(!outFile->open(QFile::WriteOnly)) {

        inFile->close();
        avcodec_free_context(&codecContext);
        av_parser_close(codecParserContext);
        av_packet_free(&packet);
        av_frame_free(&frame);
        return false;
    }

    int readLen = 0;
    bool end = false;
    char* startBuffer = new char[DECODE_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
    char* in_data = nullptr;

    do {

        readLen = inFile->read(startBuffer,DECODE_BUFFER_SIZE);

        int parserLen = readLen;
        in_data = startBuffer;

        end = (parserLen == 0);

        while (parserLen > 0 || end) {

            /**
             *   while(in_len){
             *       len = av_parser_parse2(myparser, AVCodecContext, &data, &size,
             *                                        in_data, in_len,
             *                                        pts, dts, pos);
             *       in_data += len;
             *       in_len  -= len;
             *
             *       if(size)
             *          decode_frame(data, size);
             *   }
             */
            int haveParserLen = av_parser_parse2(codecParserContext,
                                                 codecContext,
                                                 &packet->data,
                                                 &packet->size,
                                                 (const uint8_t *)in_data,
                                                 parserLen,
                                                 AV_NOPTS_VALUE,AV_NOPTS_VALUE,0);


            in_data += haveParserLen;
            parserLen -= haveParserLen;

            if(packet->size > 0 && doH264Decode(codecContext,packet,frame,outFile)) {

                qDebug() << "decode error";
                break;
            }

            if(end) {

                break;
            }
        }


    }while(readLen > 0);

    doH264Decode(codecContext,nullptr,frame,outFile);

    yuvFileSpec.setWidth(codecContext->width);
    yuvFileSpec.setHeight(codecContext->height);
    yuvFileSpec.setFormat(codecContext->pix_fmt);
    yuvFileSpec.setFps(codecContext->framerate.num);

    if(startBuffer) {

        delete [] startBuffer;
    }
    inFile->close();
    outFile->close();
    avcodec_free_context(&codecContext);
    av_parser_close(codecParserContext);
    av_packet_free(&packet);
    av_frame_free(&frame);

    return true;
}
